/* Nate Dierk
 * Comp 590 - a1
 * 8/26/2013
 */

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <unistd.h>
#include <time.h>

using namespace std;

//////////////////////////////////////////////////////////
/*
 * Functions that will run on strings. All use the argFn 
 * typedef so I can neatly stash them into a struct array.
 */

typedef bool (*argFn)(int, char**); 

struct argFnData {
  char id[32];
  argFn fn;
  int argsConsumed;
  char desc[256];
};

bool printError(int argc, char** argv){
  cout << "I'm sorry, but the option " << argv[-1] << " that you have given has not been recognized. Now terminating.\n";
  return false;
}

argFnData Error = {
  "ERROR", printError, 0, "ERROR!",
};

bool reverse(int argc, char** argv){
  char* start = argv[0];
  char* end = argv[0];

  while (0 != *end){ 
    ++end; 
  }
  --end;

  while (end > start){
    char tmp = *start;
    *start = *end;
    *end = tmp;
    ++start;
    --end;
  };
  return true;
}

argFnData Reverse = {
  "--rev",
  reverse,
  1,
  "Reverses the next argument and prints it.\n\t\tUsage: --rev <revMe>"
};

bool capitalize(int argc, char** argv){
  char* s = argv[0];
  
  while(*s){
    *s = toupper(*s);
    s++;
  }
  return true;
}

argFnData Caps = {
  "--caps",
  capitalize,
  1,
  "Capitalizes the next argument and prints it.\n\t\tUsage: --caps <capsMe>"
};

bool lowercase(int argc, char** argv){
  char* s = argv[0];

  while(*s){
    *s = tolower(*s);
    s++;
  }
  return true;
}

argFnData Lower = {
  "--lower",
  lowercase,
  1,
  "Lowercases the next argument and prints it.\n\t\tUsage: --lower <lowerMe>"
};

bool substring(int argc, char** argv){
  char* sub = argv[0];
  char* searchMe = argv[1];
  
  for (int i = 0; i < strlen(searchMe) - strlen(sub); ++i){
    bool found = true;
    
    for (int j = 0; found && j < strlen(sub); ++j){
      if (sub[j] != searchMe[i + j]){
	found = false;
      }
    }
    
    if (found) {
      argv[0] = "true";
      return true;
    }
  }

  argv[0] = "false";
  return true;
}

argFnData Substring = {
  "--sub",
  substring,
  2,
  "Prints true if next argument is a substring of the following argument.\n\t\tUsage: --sub <sub-string> <search-string>"
};

bool time(int argc, char** argv){
  time_t rawtime;
  struct tm * timeinfo;
  char buffer [80];

  time (&rawtime);
  timeinfo = localtime (&rawtime);

  strftime (buffer,80,"It's %I:%M%p EST.",timeinfo);
  puts (buffer);
  return true;
}

argFnData Time = {
  "--time",
  time,
  0,
  "Prints out the time of day in EST.\n\t\tUsage: --time"
};

argFnData* options[] = {
  &Error,
  &Time,
  &Reverse,
  &Caps,
  &Lower,
  &Substring,
  //  &Help, due to circular dependency help will be treated differently.
};

void printHelp(){
  cout << "Currently implemented options:\n";

  int numOpts = sizeof(options) / sizeof(void*);
  for (int i = 1; i < numOpts; ++i){
    cout << "\t" << options[i]->id << "\t" << options[i]->desc << "\n";
  }

  cout << endl;
}

argFnData* matchOption(char* s){
  int numOpts = sizeof(options) / sizeof(void*);

  for (int i = 0; i < numOpts; ++i){
    if (0 == strcmp(s, options[i]->id)){
      return options[i];
    }
  }
  return options[0];
}

int main(int argc, char** argv){

  // consume first arg
  cout << "\nThank you for running me! My name is " << argv[0] << ", so pleased to meet you!\n\n";
  --argc;
  ++argv;

  // loop and complete each other option
  while (argc > 0) {

    if (0 == strcmp("--help", argv[0])){
      printHelp();
      return 0;
    } else {
      argFnData option = *matchOption(argv[0]); // match option
      ++argv; // move past option in array

      if (--argc >= option.argsConsumed){

	cout << "\t" << option.id << " ";
	  
	for (int i = 0; i < option.argsConsumed; ++i){
	  cout << argv[i] << " ";
	}
	cout << ":  ";

	if (option.fn(argc, argv)){ // run option's function
	  cout << argv[0] << endl;
	  
	  //consume the arguments
	  argc -= option.argsConsumed;
	  argv += option.argsConsumed;
	}
      } else { // not enough arugments
	cout << "You didn't supply enough arguments. The option " << option.id << " requires " << option.argsConsumed << " argument(s).\n";
	return -1;
      }
    }
  }
  return 0;
}
